/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph.tasks.io;

import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

import taskgraph.Config;
import taskgraph.ports.CharInputPort;
import taskgraph.tasks.PrimitiveTask;


/**
 * Source task for writing a text file.
 *  
 * <p>The contents is put taken from the char input port as-is,
 * without filtering out line separators or any other characters,
 * and written to the file.
 * 
 * @author Armando Blancas
 * @see taskgraph.ports.CharInputPort
 */
public class WriteTextFile extends PrimitiveTask {

	private static final String DEFAULT_ENCODING = Charset.defaultCharset().name();
	private static final int    DEFAULT_CAPACITY = Config.get().bufferCapacity();
	
	private CharInputPort input;
	private String filename;
	private String charsetName = DEFAULT_ENCODING;
	private int bufferSize = DEFAULT_CAPACITY;

	/**
	 * This constructor allows creating instances as beans.
	 */
    public WriteTextFile() {
    	// do nothing
    }

	/**
     * Creates a fully configured instance.
     * 
     * @param input The char input port.
     * @param filename The full path of the file to write as String.
     */
    public WriteTextFile(final CharInputPort input, final String filename) {
    	setInput(input);
    	setFilename(filename);
    }

	/**
     * Creates a fully configured instance.
     * 
     * @param input The char input port.
     * @param filename The full path of the file to write as String.
     * @param charsetName  The name of the character encoding as String.
	 * @throws UnsupportedEncodingException 
     */
    public WriteTextFile(final CharInputPort input, 
    		             final String filename,
                         final String charsetName) 
    throws UnsupportedEncodingException {
    	setInput(input);
    	setFilename(filename);
    	setCharsetName(charsetName);
    }

	/**
	 * Gets the buffer size for reading chunks of characters.
	 * 
	 * @return the bufferSize
	 */
	public int getBufferSize() {
		return bufferSize;
	}

	/**
	 * Gets the name of the character encoding.
	 * 
	 * @return The charsetName value as {@code String}.
	 */
	public String getCharsetName() {
		return charsetName;
	}

    /**
     * Gets the name of the file to read.
     * 
	 * @return The full path of the file as String
	 */
	public String getFilename() {
		return filename;
	}
    
    /**
     * Gets the input port.
     * 
	 * @return The char input port.
	 */
	public CharInputPort getInput() {
		return input;
	}

    /**
	 * Reads the input port and writes the contents to the text file.
	 *<pre>
	 *open text file
	 *while not EOF
	 *    read characters from input port
	 *    write characters to text file
	 *close input port
	 *close text file
	 *</pre>
	 */
	@Override
    public void run() {
		PrintWriter output = null;
    	try {
    		output = new PrintWriter(
					 	 new BufferedWriter(
					 		 new OutputStreamWriter(
					 			 new FileOutputStream(filename),
					 			 charsetName),
					 		 bufferSize),
					 	false);
    		
    		char[] data = new char[bufferSize];
    		while (!Thread.currentThread().isInterrupted()) {
    			int count = input.read(data, 0, bufferSize);
            	output.write(data, 0, count);
            }
        } catch (InterruptedIOException e) {
        	// interruption request
        } catch (EOFException e) {
        	// we're done reading
        } catch (IOException e) {
        	// file not found, pipe synch error
            log.error("WriteTextFile#run()", e);
        } finally {
        	input.close();
        	if (output != null)
        		output.close();
        }
    }

	/**
	 * Sets the buffer size for reading chunks of characters.
	 * 
	 * <p>The argument should not be less than the default. If it is, the
	 * default size is used instead.
	 * 
	 * @param bufferSize the bufferSize to set
	 */
	public void setBufferSize(final int bufferSize) {
		this.bufferSize = 
			(bufferSize < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : bufferSize;
	}

	/**
	 * Sets the name of the character encoding.
	 * 
	 * @param charsetName The {@code String} value to set as charset name.
	 * @throws UnsupportedEncodingException 
	 */
	public void setCharsetName(final String charsetName)
	throws UnsupportedEncodingException {
    	if (charsetName == null)
    		throw new IllegalArgumentException("charsetName == null");
		if (!Charset.isSupported(charsetName)) {
			throw new UnsupportedEncodingException (charsetName);
		}
		this.charsetName = charsetName;
	}

	/**
	 * Sets the name of the file to read.
	 * 
	 * @param filename The full path of the file as String.
	 */
	public void setFilename(final String filename) {
    	if (filename == null)
    		throw new IllegalArgumentException("filename == null");
		this.filename = filename;
	}

	/**
	 * Sets the input port.
	 * 
	 * @param input The char input port.
	 */
    public void setInput(final CharInputPort input) {
    	if (input == null)
    		throw new IllegalArgumentException("input == null");
 		this.input = input;
	}

}
